using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.IO;

namespace ItdavanyViewer
{
    public partial class XNAView : UserControl, IServiceProvider
    {
        protected GraphicsDeviceService graphics;
        protected ContentManager content;
        private Timer GameTimer = null;
        private TimeSpan totalTime = new TimeSpan();
        private TimeSpan totalRealTime = new TimeSpan();
        private TimeSpan lastRealTime = new TimeSpan();        
        private System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
        private TimeSpan targetElapsedTime;

        public XNAView() : this(Directory.GetCurrentDirectory())
        {
            
        }

        public XNAView(string root)
        {            
            InitializeComponent();

            GameTimer = new Timer();
            GameTimer.Tick += new EventHandler(OnViewportDraw);
            GameTimer.Tick += new EventHandler(OnXnaUpdate);
            TargetElapsedTime = TimeSpan.FromMilliseconds(16);

            this.graphics = new GraphicsDeviceService(this);
            this.graphics.CreateDevice();
            this.content = new ContentManager(this, root);
        }

        private void XNAView_Load(object sender, EventArgs e)
        {
            GameTimer.Start();
            timer.Start();

            LoadGraphicsContent();
        }

        public TimeSpan TargetElapsedTime
        {
            get
            {
                return targetElapsedTime;
            }
            set
            {
                targetElapsedTime = value;
                GameTimer.Interval = 
                    Convert.ToInt32(targetElapsedTime.TotalMilliseconds);                
            }
        }

        public virtual string RootDirectory
        {
            get
            {
                return content.RootDirectory;
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        public virtual void Draw()
        {
        }

        public void ResetGraphicsDevice()
        {
            //timer.Stop();
            //GameTimer.Stop();

            if (this.Width == 0 || this.Height == 0 
                || graphics.GraphicsDevice.IsDisposed)
                return;

            graphics.ResetDevice();

            //timer.Start();
            //GameTimer.Start();

            OnViewportDraw(null, EventArgs.Empty);
        }

        private void OnViewportDraw(object sender, EventArgs e)
        {
            if (graphics.BeginDraw())
            {
                Draw();
                graphics.EndDraw();
            }
        }



        private void OnXnaUpdate(object sender, EventArgs e)
        {            
            TimeSpan elapsed = TimeSpan.FromMilliseconds(GameTimer.Interval);
            totalTime += elapsed;
                        
            totalRealTime = timer.Elapsed;
            TimeSpan realElapsed = totalRealTime - lastRealTime;
            lastRealTime = totalRealTime;            

            XnaUpdate(new GameTime(totalRealTime, realElapsed, totalTime, elapsed));
        }

        public virtual void XnaUpdate(GameTime gameTime)
        {
        }

        #region IServiceProvider Members

        public new object GetService(Type serviceType)
        {
            if (serviceType == typeof(Microsoft.Xna.Framework.Graphics.IGraphicsDeviceService))
            {
                return this.graphics;
            }
            else
            {
                return base.GetService(serviceType);
            }
        }

        #endregion


        protected virtual void LoadGraphicsContent()
        {
        }

        protected virtual void UnloadGraphicsContent()
        {
            content.Unload();
        }
    }

    public class GraphicsDeviceService : IGraphicsDeviceService, IGraphicsDeviceManager
    {
        private GraphicsDevice device;
        private Control controlToRender;

        public GraphicsDeviceService(Control control)
        {
            this.controlToRender = control;
        }

        #region IGraphicsDeviceService Members

        public event EventHandler DeviceCreated;
        public event EventHandler DeviceDisposing;
        public event EventHandler DeviceReset;
        public event EventHandler DeviceResetting;

        public GraphicsDevice GraphicsDevice
        {
            get { return this.device; }
        }

        #endregion

        #region IGraphicsDeviceManager Members

        public bool BeginDraw()
        {
            if (device == null)
                throw new InvalidOperationException(
                    "CreateDevice has not been called.", 
                    new NullReferenceException()
                    );

            if (device.GraphicsDeviceStatus == GraphicsDeviceStatus.NotReset)
                device.Reset();

            return device.GraphicsDeviceStatus == GraphicsDeviceStatus.Normal;
        }

        public void CreateDevice()
        {
            PresentationParameters pp = new PresentationParameters();
            pp.IsFullScreen = false;
            pp.BackBufferCount = 1;
            pp.BackBufferHeight = this.controlToRender.ClientSize.Height;
            pp.BackBufferWidth = this.controlToRender.ClientSize.Width;
            pp.BackBufferFormat = SurfaceFormat.Color;
            pp.SwapEffect = SwapEffect.Discard;

            if (this.device == null)
            {
                this.device = new GraphicsDevice(
                    GraphicsAdapter.DefaultAdapter,
                    DeviceType.Hardware,
                    this.controlToRender.Handle,
                    CreateOptions.HardwareVertexProcessing, pp);
                if (DeviceCreated != null)
                    DeviceCreated.Invoke(device, EventArgs.Empty);
                this.device.Disposing += DeviceDisposing;
                this.device.DeviceReset += DeviceReset;
                this.device.DeviceResetting += DeviceResetting;
            }
            else
            {
                this.device.Reset(pp);
            }
        }

        public void EndDraw()
        {
            device.Present();
        }

        #endregion

        public void ResetDevice()
        {
            if (device == null)
                throw new InvalidOperationException(
                    "CreateDevice has not been called.",
                    new NullReferenceException()
                    );

            CreateDevice();
        }
    }
}
